Skip to content

Conversation

@eryue0220
Copy link

resolve #40

@vercel
Copy link

vercel bot commented Feb 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Feb 4, 2026 4:17pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 4, 2026 4:17pm
npmx-lunaria Ignored Ignored Feb 4, 2026 4:17pm

Request Review

@github-actions
Copy link

github-actions bot commented Feb 4, 2026

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
lunaria/files/en-GB.json Localization changed, will be marked as complete. 🔄️
lunaria/files/en-US.json Source changed, localizations will be marked as outdated.
lunaria/files/zh-CN.json Localization changed, will be marked as complete. 🔄️
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds package deprecation support across UI, CLI and server: a new Vue 3 component DeprecatePackageModal exposing open() and close() for deprecating a package or specific version with a custom reason; integration into the package page for owners; a new package:deprecate operation type with schema and type updates; CLI helper packageDeprecate; server executor wiring to call the CLI helper; i18n entries for en, en-GB, en-US and zh-CN; accessibility tests for the modal; and a .gitignore addition for *.junit.xml.

Suggested reviewers

  • danielroe
  • Kai-ros
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning One change to .gitignore adding *.junit.xml appears unrelated to the deprecation feature; all other changes directly support the deprecation functionality. Remove the .gitignore entry for *.junit.xml or move it to a separate pull request addressing test coverage configuration.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description 'resolve #40' is minimal but directly relates to the changeset which implements package deprecation with custom reason.
Linked Issues check ✅ Passed The pull request comprehensively implements package deprecation with custom reason support across frontend, CLI, and i18n layers, meeting all objectives from #40.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 4, 2026

Codecov Report

❌ Patch coverage is 80.89888% with 17 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
app/pages/package/[...package].vue 0.00% 13 Missing and 3 partials ⚠️
app/components/Package/DeprecatePackageModal.vue 98.63% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
app/pages/package/[...package].vue (1)

1156-1169: Remove duplicate CSS classes.

The button has redundant inline-flex items-center (conflicting with flex) and w-full appearing twice in the class string.

🧹 Proposed fix
           <button
             type="button"
-            class="flex items-center justify-center w-full px-3 py-1.5 bg-bg-subtle rounded text-sm font-mono text-red-400 hover:text-red-500 transition-colors inline-flex items-center gap-1.5 w-full"
+            class="flex items-center justify-center gap-1.5 w-full px-3 py-1.5 bg-bg-subtle rounded text-sm font-mono text-red-400 hover:text-red-500 transition-colors"
             `@click`="deprecateModal?.open()"
           >

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/pages/package/[...package].vue (1)

1245-1256: Clean up duplicate CSS classes.

The button has redundant classes: both flex and inline-flex, and w-full appears twice.

♻️ Suggested fix
           <button
             type="button"
-            class="flex items-center justify-center w-full px-3 py-1.5 bg-bg-subtle rounded text-sm font-mono text-red-400 hover:text-red-500 transition-colors inline-flex items-center gap-1.5 w-full"
+            class="flex items-center justify-center gap-1.5 w-full px-3 py-1.5 bg-bg-subtle rounded text-sm font-mono text-red-400 hover:text-red-500 transition-colors"
             `@click`="deprecateModal?.open()"
           >

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/components/Package/DeprecatePackageModal.vue (2)

24-28: Use the imported t function instead of $t in the script block.

The component destructures t from useI18n() on line 12, but this computed uses $t. For consistency within the script setup, use the imported t function.

♻️ Proposed fix
 const modalTitle = computed(() =>
   deprecateVersion.value
-    ? `${$t('package.deprecation.modal.title')} ${props.packageName}@${deprecateVersion.value}`
-    : `${$t('package.deprecation.modal.title')} ${props.packageName}`,
+    ? `${t('package.deprecation.modal.title')} ${props.packageName}@${deprecateVersion.value}`
+    : `${t('package.deprecation.modal.title')} ${props.packageName}`,
 )

121-127: Consider removing inline focus-visible utilities from buttons.

Per project convention, focus-visible styling for buttons is applied globally via main.css with button:focus-visible { outline: 2px solid var(--accent); ... }. The inline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 utilities override this global rule.

If this modal requires different focus styling, consider whether it should be consistent with the rest of the application or if an exception is warranted.

Based on learnings: "In the npmx.dev project, ensure that focus-visible styling for button and select elements is implemented globally in app/assets/main.css... Do not apply per-element inline utility classes."

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/components/Package/DeprecatePackageModal.vue (2)

69-69: Consider adding optional chaining for defensive access.

If state.value.operations is ever undefined (e.g., during initialisation or after an error), this line would throw. Adding optional chaining aligns with the strict type-safety guideline.

🛡️ Proposed fix
-    const completedOp = state.value.operations.find(op => op.id === operation.id)
+    const completedOp = state.value.operations?.find(op => op.id === operation.id)

122-128: Remove inline focus-visible utilities on buttons to rely on global styling.

Both buttons in this component have custom focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 classes. The project uses a global CSS rule for button focus-visible styling in main.css. Removing these inline utilities ensures consistency across the codebase.

This applies to the button on lines 164-175 as well.

♻️ Proposed fix
       <button
         type="button"
-        class="w-full px-4 py-2 font-mono text-sm text-fg-muted bg-bg-subtle border border-border rounded-md transition-colors duration-200 hover:text-fg hover:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
+        class="w-full px-4 py-2 font-mono text-sm text-fg-muted bg-bg-subtle border border-border rounded-md transition-colors duration-200 hover:text-fg hover:border-border-hover"
         `@click`="close"
       >

And for the deprecate button:

       <button
         type="button"
         :disabled="isDeprecating || !deprecateMessage.trim()"
-        class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-colors duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
+        class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-colors duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed"
         `@click`="handleDeprecate"
       >

Based on learnings: "In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css... individual buttons or selects in Vue components should not rely on inline focus-visible utility classes."

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…feat/resolve-40

* 'feat/resolve-40' of github.com:eryue0220/npmx.dev:
  Update app/components/Package/DeprecatePackageModal.vue
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Comment on lines +151 to +202
describe('form state', () => {
it('deprecate button is disabled when message is empty', async () => {
const component = await mountSuspended(DeprecatePackageModal, {
props: { packageName: 'pkg' },
...mountOptions,
})

const buttons = component.findAll('button[type="button"]')
const submitBtn = buttons.find(b => b.attributes('disabled') !== undefined)
expect(submitBtn).toBeDefined()
})

it('deprecate button is enabled when message is filled and connected', async () => {
mockIsConnected.value = true
const component = await mountSuspended(DeprecatePackageModal, {
props: { packageName: 'pkg' },
...mountOptions,
})

await component.find('#deprecate-message').setValue('Deprecated, use foo instead')
await component.vm.$nextTick()

const buttons = component.findAll('button[type="button"]')
const submitBtn = buttons.find(b => b.attributes('disabled') === undefined)
expect(submitBtn).toBeDefined()
})

it('handleDeprecate does nothing when not connected', async () => {
mockIsConnected.value = false
const component = await mountSuspended(DeprecatePackageModal, {
props: { packageName: 'pkg' },
...mountOptions,
})

await component.find('#deprecate-message').setValue('message')
await component.vm.$nextTick()
await getVM(component).handleDeprecate?.()

expect(mockAddOperation).not.toHaveBeenCalled()
})

it('handleDeprecate does nothing when message is empty', async () => {
const component = await mountSuspended(DeprecatePackageModal, {
props: { packageName: 'pkg' },
...mountOptions,
})

await getVM(component).handleDeprecate?.()

expect(mockAddOperation).not.toHaveBeenCalled()
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Target the deprecate action button explicitly in enable/disable assertions.
The current selector can match the cancel button, so these tests can pass even if the deprecate button’s state is wrong. Prefer a dedicated selector (e.g. a data-testid on the deprecate button) and assert directly on it.

Proposed fix (use a dedicated selector)
-      const buttons = component.findAll('button[type="button"]')
-      const submitBtn = buttons.find(b => b.attributes('disabled') !== undefined)
-      expect(submitBtn).toBeDefined()
+      const submitBtn = component.get('[data-testid="deprecate-submit"]')
+      expect(submitBtn.attributes('disabled')).toBeDefined()
-      const buttons = component.findAll('button[type="button"]')
-      const submitBtn = buttons.find(b => b.attributes('disabled') === undefined)
-      expect(submitBtn).toBeDefined()
+      const submitBtn = component.get('[data-testid="deprecate-submit"]')
+      expect(submitBtn.attributes('disabled')).toBeUndefined()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add package deprecation with custom reason

1 participant